package tmhprediction.eval;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;

import org.apache.commons.io.FileUtils;

import tmhprediction.main.TMHResultMap;

/***
 * Klasse berechnet die finalen scores. wie viele helixe (nicht einzelne positionen, sondern zusammenhaengende helixe) korrekt bestimmt wurden.
 * @author steckl
 *
 */
public class TMHHelixEvaluator {
	
	public final static short INDEX_OBS = 0;
	public final static short INDEX_PRED = 1;
	public final static short INDEX_CONFIDENCE = 2;
	
	static short dWindowSize = 3;
	
	
	/***
	 * Funktion zum testen
	 * @param args
	 */
	public static void main(String[] args) {
		TMHResultMap testResultMap = new TMHResultMap();
		double[] vals = new double[3];
		
		double[] obs1  = {1,1,1,1,1,0,1,1,1,1,1,0,0,0};
		double[] pred1 = {0,0,1,1,1,0,1,1,1,1,1,0,0,0};
		
		for(int i = 0; i < obs1.length; i++) {
			vals[0] = obs1[i];
			vals[1] = pred1[i];
			vals[2] = 0;
			
			testResultMap.put("eins_"+i, vals.clone());
		}
		
		double[] obs2  = {0,0,0,0,0,0,0,0};
		double[] pred2 = {0,0,0,0,0,0,1,1};
		
		for(int i = 0; i < obs2.length; i++) {
			vals[0] = obs2[i];
			vals[1] = pred2[i];
			vals[2] = 0;
			
			testResultMap.put("zwei_"+i, vals.clone());
		}
		
		berechneFormeln(finalHelixEvaluation(testResultMap), "OUTPUT FILE HERE");
	}
	
	/***
	 * Calculates the overlap of observed and predicted TMHs
	 * @param result
	 * key: NAME_POS
	 * value: (OBSERVED (0 oder 1), PREDICTED (0 oder 1), CONFIDENCE (0-1 kommazahl))
	 * @return a hash-map containing name of protein 
	 */
	public static HashMap<String, ArrayList<short[]>> finalHelixEvaluation(TMHResultMap result) {

		HashMap<String, ArrayList<short[]>> allResultsMap = new HashMap<String, ArrayList<short[]>>();
		
		
		String name;
		int pos = 0;
		int nameLength;
		short[] werte;
		
		short[] invalid = {-1,-1};
		
		for(String sProtein_pos : result.keySet())
		{
			
			werte = new short[3];
			werte[0] =  (short) result.get(sProtein_pos)[0];  // observed
			werte[1] = (short) result.get(sProtein_pos)[1];   // predicted
			double confidence = result.get(sProtein_pos)[2];
			confidence = confidence * 10.0;
			short conf = (short) confidence;
			if(confidence == 1.0)
			{
				conf = 9;
			}
			
			werte[2] = conf; //confidence
			
			try {
				nameLength = sProtein_pos.lastIndexOf("_");
				name = sProtein_pos.substring(0,nameLength);
				
				try {
					pos = Integer.parseInt(sProtein_pos.substring(nameLength+1));
				} catch(Exception ex) {
					pos=0;
					System.err.println("Exception, but normal for first SVM's statistic, since it has no per residue result");
					//ex.printStackTrace();
				}
				
				// new protein
				if (allResultsMap.containsKey(name) == false) {
					allResultsMap.put(name, new ArrayList<short[]>());
				}
				
				// fill missing elements with invalid -> could happen, that elements are not sorted
				while(allResultsMap.get(name).size() < pos+1) {
					allResultsMap.get(name).add(invalid);
				}
					
					
				allResultsMap.get(name).set(pos,werte.clone());
			
			} catch(Exception ex) {
				System.err.println("Exception, but normal for first SVM's statistic, since it has no per residue result");
			}
			
			
			

			
			
		}
		
		return allResultsMap;
		
	}
	
	/***
	 * berechnet die Scores (qObs, qPred und qOk) nach Formeln von Jonas
	 * @param allResultsMap
	 */
	public static void berechneFormeln(HashMap<String, ArrayList<short[]>> allResultsMap, String outFile) {
		int corrPredHelices = 0;
		int obsHelices = 0;
		int predHelices = 0;
		int corrPredHelicesOverall = 0;
		int obsHelicesOverall = 0;
		int predHelicesOverall = 0;
		int deltaSumTmh = 0; // nur f�r die, die mindestens einen TMH haben
		
		int deltaSum = 0;	
		int numTmhProteins = 0;
		
		for(String sProtName : allResultsMap.keySet()) {
			corrPredHelices = calcCorrPredHelices(allResultsMap.get(sProtName));
			obsHelices = calcHelicesInSeries(allResultsMap.get(sProtName), 0);
			predHelices = calcHelicesInSeries(allResultsMap.get(sProtName), 1);
			
			
			try {
				FileUtils.writeStringToFile(new File(outFile),"name: "+sProtName+"; corr: "+corrPredHelices+"; obs: "+obsHelices+"; pred:"+predHelices+"\n", true);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			
			if(corrPredHelices == obsHelices && corrPredHelices == predHelices) {
				if(obsHelices > 0) {
					deltaSumTmh++;
				}
				deltaSum++;
			}
			
			corrPredHelicesOverall += corrPredHelices;
			obsHelicesOverall += obsHelices;
			predHelicesOverall += predHelices;
			
			if(obsHelices > 0) {
				numTmhProteins++;
			}
		}
		
		float qObs = ((float)corrPredHelicesOverall)/(float)obsHelicesOverall;
		float qPred = ((float)corrPredHelicesOverall)/(float)predHelicesOverall;
		float qOk = (float)deltaSum / (float)allResultsMap.size();
		float qOkTmh = (float)deltaSumTmh / (float)numTmhProteins;
		
		try {
			FileUtils.writeStringToFile(new File(outFile), "Q_obs: " +qObs + "\nQ_pred: " + qPred + "\nQ_ok: " + qOk +"\nQ_ok_tmh: "+qOkTmh+"\n", true);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
//		System.out.println("metrics: qObs: "+qObs+"; qPred: "+qPred+"; qOk:"+qOk);

	}
	
	
	public static double[] berechneFormelnSilent(HashMap<String, ArrayList<short[]>> allResultsMap) {
		int corrPredHelices = 0;
		int obsHelices = 0;
		int predHelices = 0;
		int corrPredHelicesOverall = 0;
		int obsHelicesOverall = 0;
		int predHelicesOverall = 0;
		int deltaSumTmh = 0; // nur f�r die, die mindestens einen TMH haben
		
		int deltaSum = 0;	
		int numTmhProteins = 0;
		
		for(String sProtName : allResultsMap.keySet()) {
			corrPredHelices = calcCorrPredHelices(allResultsMap.get(sProtName));
			obsHelices = calcHelicesInSeries(allResultsMap.get(sProtName), 0);
			predHelices = calcHelicesInSeries(allResultsMap.get(sProtName), 1);
				
			if(corrPredHelices == obsHelices && corrPredHelices == predHelices) {
				if(obsHelices > 0) {
					deltaSumTmh++;
				}
				deltaSum++;
			}
			
			corrPredHelicesOverall += corrPredHelices;
			obsHelicesOverall += obsHelices;
			predHelicesOverall += predHelices;
			
			if(obsHelices > 0) {
				numTmhProteins++;
			}
		}
		
		float qObs = ((float)corrPredHelicesOverall)/(float)obsHelicesOverall;
		float qPred = ((float)corrPredHelicesOverall)/(float)predHelicesOverall;
		float qOk = (float)deltaSum / (float)allResultsMap.size();
		float qOkTmh = (float)deltaSumTmh / (float)numTmhProteins;
		
		return new double[]{qOkTmh, qObs, qPred};
	}
	
	
	
	/**
	 * berechnet die korrekt vorhergesagten helixes (die mindestens dWindowSize �berlappen)
	 * @param protResMap
	 * @return
	 */
	private static int calcCorrPredHelices(ArrayList<short[]> protResMap) {
		
		int hits = 0;
		int windowSize = dWindowSize;
		int currOverlap = 0;
		
		short obs;
		short pred;
		
		for(int i = 0; i < protResMap.size(); i++) {
			obs = protResMap.get(i)[0];
			pred = protResMap.get(i)[1];

			if(obs == pred && obs == 1) {
				currOverlap++;
				
				if(currOverlap == windowSize) {
					hits++;
					currOverlap = 0;
					
					while(protResMap.get(i)[0] == 1 || protResMap.get(i)[1] == 1) {
						
						i++;
						if(i>=protResMap.size()) {
							break;
						}
					}
				}
			} else {
				currOverlap = 0;
			}
		}
		
		return hits;
		
	}

	/***
	 * berechnet die anzahl der Helixes in einer sequenz
	 * @param protResMap
	 * @param obsOrPred 0 f�r suche in observed, 1 f�r suche in predicted
	 * @return
	 */
	private static int calcHelicesInSeries(ArrayList<short[]> protResMap, int obsOrPred) {
		
		int hits = 0;
		
		short obs;
		short last = 0;
		
		for(int i = 0; i < protResMap.size(); i++) {
			obs = protResMap.get(i)[obsOrPred];
			
			if(last == 0 && obs == 1) {
				hits++;
			}
			
			last = obs;
			
		}
		
		return hits;
		
	}

}
